import base64

import pickle
from django.shortcuts import render

# Create your views here.
from django_redis import get_redis_connection
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from cart.serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer, CartSelectAllSerializer
from cart import constants
from goods.models import SKU


class CartSelectAllView(APIView):
    """
    购物车全选
    """
    def perform_authentication(self, request):
        """
        重写父类的用户验证方法，不在进入视图前就检查JWT
        """
        pass

    def put(self, request):
        """
        购物车全选
        """
        # 1. 获取勾选状态并进行校验
        serializer = CartSelectAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data['selected']

        # 2. 获取user
        try:
            user = request.user
        except Exception:
            user = None

        # 3. 设置购物车中商品全部为勾选
        if user and user.is_authenticated:
            # 3.1 如果用户已登录，设置redis购物车中商品全部为勾选
            redis_conn = get_redis_connection('cart')
            cart_key = 'cart_%s' % user.id

            # 获取用户购物车所有商品id
            sku_ids = redis_conn.hkeys(cart_key)

            # 将sku_ids添加到用户购物车勾选商品数据中
            cart_selected_key = 'cart_selected_%s' % user.id
            if selected:
                redis_conn.sadd(cart_selected_key, *sku_ids)
            else:
                redis_conn.srem(cart_selected_key, *sku_ids)

            return Response({'message': 'OK'})
        else:
            response = Response({'message': 'OK'})
            # 3.2 如果用户未登录，设置cookie购物车中商品全部为勾选
            cookie_cart = request.COOKIES.get('cart')

            if not cookie_cart:
                return Response

            if cookie_cart:
                cart_dict = pickle.loads(base64.b64decode(cookie_cart))

            if not cart_dict:
                return response

            for sku_id in cart_dict.keys():
                cart_dict[sku_id]['selected'] = selected

            # 4. 返回应答，全选成功
            cookie_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('cart', cookie_data, expires=constants.CART_COOKIE_EXPIRES)
            return response


class CartView(APIView):
    """
    购物车
    """
    def perform_authentication(self, request):
        """
        重写父类的用户验证方法，不在进入视图前就检查JWT
        """
        pass

    def delete(self, request):
        """
        购物车记录删除
        """
        # 1. 获取sku_id并进行校验
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data['sku_id']

        # 2. 获取user
        try:
            user = request.user
        except Exception:
            user = None

        # 3. 删除用户的购物车记录
        if user is not None and user.is_authenticated:
            # 3.1 如果用户已登录，删除redis中对应的购物车记录
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()

            # 删除购物车记录中对应商品id和count hash
            cart_key = 'cart_%s' % user.id
            # hdel(key, *fields): 删除hash中的指定属性和值，如果属性field不存在，直接忽略
            pl.hdel(cart_key, sku_id)

            # 删除购物车记录中对应商品勾选状态 set
            cart_selected_key = 'cart_selected_%s' % user.id
            # srem(key, *values): 删除set中指定元素，如果元素不存在，直接忽略
            pl.srem(cart_selected_key, sku_id)

            pl.execute()

            # 返回应答
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            # 3.2 如果用户未登录，删除cookie中对应的购物车记录
            response = Response(status=status.HTTP_204_NO_CONTENT)
            cookie_cart = request.COOKIES.get('cart')  # None

            if cookie_cart is None:
                return response

            # 解析购物车中数据
            cart_dict = pickle.loads(base64.b64decode(cookie_cart))  # {}

            if not cart_dict:
                return response

            # 删除购物车中对应商品数据
            if sku_id in cart_dict:
                del cart_dict[sku_id]
                cookie_data = base64.b64encode(pickle.dumps(cart_dict)).decode()  # str

                # 设置购物车cookie信息
                response.set_cookie('cart', cookie_data, expires=constants.CART_COOKIE_EXPIRES)

            # 4. 返回应答，status=204
            return response

    def put(self, request):
        """
        购物车记录更新:
        """
        # 1. 获取参数并进行校验(sku_id, count, selected)
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']

        # 2. 获取user
        try:
            # 获取user时会执行使用认证机制进行认证，因为客户端传递了jwt 数据
            # 如果认证失败，这句代码会报错
            user = request.user
        except Exception:
            user = None

        # 3. 更新购物车记录
        if user is not None and user.is_authenticated:
            # 3.1 如果用户已登录，更新redis中购物车记录
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()

            # 设置用户购物车中商品的id和对应数量count hash
            cart_key = 'cart_%s' % user.id
            # hset(key, field, value): 设置hash中某个属性值
            pl.hset(cart_key, sku_id, count)

            # 设置用户购物车中商品的勾选状态 set
            cart_selected_key = 'cart_selected_%s' % user.id
            if selected:
                # 勾选
                # sadd(key, *values): 向set中添加元素
                pl.sadd(cart_selected_key, sku_id)
            else:
                # 不勾选
                # srem(name, *values): 从set中移除元素，如果元素不存在，直接忽略
                pl.srem(cart_selected_key, sku_id)

            pl.execute()

            # 返回应答
            return Response(serializer.data)
        else:
            response = Response(serializer.data)
            # 3.2 如果用户未登录，更新cookie中购物车记录
            cookie_cart = request.COOKIES.get('cart')  # None

            if cookie_cart is None:
                return response

            # 解析购物车中数据
            cart_dict = pickle.loads(base64.b64decode(cookie_cart))  # {}

            if not cart_dict:
                return response

            # 设置用户购物车商品数量count和勾选状态
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 4. 返回应答
            # 处理
            cookie_data = base64.b64encode(pickle.dumps(cart_dict)).decode()  # str

            # 设置购物车cookie信息
            response.set_cookie('cart', cookie_data, expires=constants.CART_COOKIE_EXPIRES)
            return response

    def get(self, request):
        """
        获取用户的购物车记录
        """
        # 1. 获取user
        try:
            user = request.user
        except Exception:
            user = None

        # 2. 获取用户的购物车记录
        if user:
            # 2.1 如果用户已登录，从redis中获取用户的购物车记录
            redis_conn = get_redis_connection('cart')

            # 获取用户购物车中商品的id和数目
            # {
            #     '<sku_id>': '<count>',
            #     ...
            # }
            cart_key = 'cart_%s' % user.id
            redis_cart = redis_conn.hgetall(cart_key)

            # 获取购物车被选中的商品的id
            cart_selected_key = 'cart_selected_%s' % user.id
            redis_cart_selected = redis_conn.smembers(cart_selected_key)

            # 组织数据
            # {
            #     '<sku_id>': {
            #         'count': '<count>',
            #         'selected': '<selected>'
            #     },
            #     ...
            # }
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_cart_selected
                }

        else:
            # 2.2 如果用户未登录，从cookie中获取用户的购物车记录
            cookie_cart = request.COOKIES.get('cart')

            if cookie_cart:
                cart_dict = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart_dict = {}

        # 3. 根据购物车记录获取对应商品的信息
        skus = SKU.objects.filter(id__in=cart_dict.keys())
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        # 4. 将商品数据序列化并进行返回
        serializer = CartSKUSerializer(skus, many=True)
        return Response(serializer.data)

    def post(self, request):
        """
        购物车记录添加
        """
        # 1. 获取参数(sku_id, count, selected)并进行参数校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']

        # 获取登录用户
        try:
            user = request.user
        except Exception as e:
            user = None

        if user and user.is_authenticated:
            # 2. 保存用户的购物车记录
            redis_conn = get_redis_connection('cart')
            pipeline = redis_conn.pipeline()
            cart_key = 'cart_%s' % user.id

            # 保存购物车中商品及数量
            pipeline.hincrby(cart_key, sku_id, count)

            # 保存购物车中商品的选中状态
            cart_selected_key = 'cart_selected_%s' % user.id
            if selected:
                pipeline.sadd(cart_selected_key, sku_id)

            pipeline.execute()

            # 3. 返回应答，保存购物车记录成功
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            # 获取客户端发送的cookie信息
            cookie_cart = request.COOKIES.get('cart')

            if cookie_cart:
                # 对cookie数据进行解析
                cart_dict = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart_dict = {}

            if sku_id in cart_dict:
                count += cart_dict[sku_id]['count']

            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 对cart_dict数据进行处理
            cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()

            response = Response(serializer.data, status=status.HTTP_201_CREATED)
            response.set_cookie('cart', cart_data, max_age=constants.CART_COOKIE_EXPIRES)
            return response